Obafemi Emmanuel

Riverpod - Modern State Management in Flutter

Published 3 months ago

State management is a crucial aspect of Flutter app development. While the framework provides multiple options, Riverpod stands out as a modern, flexible, and scalable state management solution. In this blog, we will explore Riverpod in detail, understand its advantages, and implement it in a Flutter project.


What is Riverpod?

Riverpod is a state management library created by Rémi Rousselet, the author of Provider. It builds on the strengths of Provider while eliminating some of its limitations. It provides a more robust and testable way to manage state in Flutter applications.


Key Features of Riverpod:

  • Compile-time safety: Eliminates common errors found in Provider.
  • Scalability: Suitable for small and large applications.
  • AutoDispose feature: Automatically disposes of unused state.
  • Support for Dependency Injection: Easily manage dependencies across the app.
  • Declarative & Functional: Encourages a functional approach to state management.

Setting Up Riverpod in a Flutter Project

To get started with Riverpod, add the necessary dependencies to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.0.0

Then, run:

dart pub get

Understanding Riverpod Providers

In Riverpod, everything revolves around Providers, which are immutable representations of a state that can be shared across the application. Some common provider types include:


1. Provider (Read-Only State)

This is used when you need to expose a read-only state:

import 'package:flutter_riverpod/flutter_riverpod.dart';

final greetingProvider = Provider<String>((ref) {
  return 'Hello, Riverpod!';
});

2. StateProvider (Mutable State)

Use StateProvider when you need a mutable state:

final counterProvider = StateProvider<int>((ref) => 0);

In the UI, access and update the state:

Consumer(
  builder: (context, ref, child) {
    final count = ref.watch(counterProvider);
    return Column(
      children: [
        Text('Count: \$count'),
        ElevatedButton(
          onPressed: () => ref.read(counterProvider.notifier).state++,
          child: Text('Increment'),
        ),
      ],
    );
  },
);

3. FutureProvider (Asynchronous Data Handling)

FutureProvider is useful for handling asynchronous operations, such as fetching data from an API:

final dataProvider = FutureProvider<String>((ref) async {
  await Future.delayed(Duration(seconds: 2));
  return 'Fetched Data';
});

In the UI:

Consumer(
  builder: (context, ref, child) {
    final asyncValue = ref.watch(dataProvider);
    return asyncValue.when(
      data: (data) => Text(data),
      loading: () => CircularProgressIndicator(),
      error: (err, stack) => Text('Error: \$err'),
    );
  },
);

4. StateNotifierProvider (Advanced State Management)

When working with complex states, StateNotifierProvider is a better choice:

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);

  void increment() => state++;
}

final counterNotifierProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

Usage in UI:

Consumer(
  builder: (context, ref, child) {
    final count = ref.watch(counterNotifierProvider);
    return Column(
      children: [
        Text('Count: \$count'),
        ElevatedButton(
          onPressed: () => ref.read(counterNotifierProvider.notifier).increment(),
          child: Text('Increment'),
        ),
      ],
    );
  },
);


import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = StateProvider<int>((ref) => 0);

void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterScreen(),
    );
  }
}

class CounterScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Scaffold(
      appBar: AppBar(title: Text('Riverpod Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Count: \$count', style: TextStyle(fontSize: 24)),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => ref.read(counterProvider.notifier).state++,
              child: Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

Conclusion

Riverpod is a powerful and modern approach to state management in Flutter. It offers compile-time safety, scalability, and ease of use, making it a preferred choice for many developers. By using Riverpod, you can write cleaner, more maintainable code while improving the performance of your Flutter applications.

Start integrating Riverpod in your projects today and experience its benefits firsthand!


Further Reading:


Leave a Comment


Choose Colour